package com.test.A02;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.AnnotationConfigUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.mvc.Controller;


public class A02 {
	
	 public static void main(String[] args) {
		// testClassPathXmlApplicationContext();
		// testFileSystemXmlApplicationContext();
		// testAnnotationConfigApplicationContext();
		  testAnnotationConfigServletWebServerApplicationContext();
		 //test5();
	}
	 
	 
	 /***
	  * ClassPathXmlApplicationContext和FileSystemXmlApplicationContext实现的逻辑
	  */
	 public static void test5() {
		 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
	        System.out.println("读取之前...");
	        for (String name : beanFactory.getBeanDefinitionNames()) {
	            System.out.println(name);
	        }
	        System.out.println("读取之后...");
	        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
	        reader.loadBeanDefinitions(new FileSystemResource("src\\main\\resources\\a02.xml"));//filepath读取配置文件的逻辑
	        // reader.loadBeanDefinitions(new ClassPathResource("a02.xml"));//classpath读取配置文件的逻辑
	        for (String name : beanFactory.getBeanDefinitionNames()) {
	            System.out.println(name);
	        }
	 }
	  
	  // ⬇️较为经典的容器, 基于 classpath 下 xml 格式的配置文件来创建
	    public static void testClassPathXmlApplicationContext() {
	        ClassPathXmlApplicationContext context =
	                new ClassPathXmlApplicationContext("a02.xml");

	        for (String name : context.getBeanDefinitionNames()) {
	            System.out.println(name);
	        }

	        System.out.println(context.getBean(Bean2.class).getBean1());
	    }
	    
	 // ⬇️基于磁盘路径下 xml 格式的配置文件来创建
	    public static void testFileSystemXmlApplicationContext() {
	        FileSystemXmlApplicationContext context =
	                new FileSystemXmlApplicationContext(
	                        "src\\main\\resources\\a02.xml");
	        for (String name : context.getBeanDefinitionNames()) {
	            System.out.println(name);
	        }

	        System.out.println(context.getBean(Bean2.class).getBean1());
	    }

	    // ⬇️较为经典的容器, 基于 java 配置类来创建
	    public static void testAnnotationConfigApplicationContext() {
	        AnnotationConfigApplicationContext context =  //AnnotationConfigApplicationContext 可以默认的加一些后处理器
	                new AnnotationConfigApplicationContext(Config.class);//读取的是配置类

	        for (String name : context.getBeanDefinitionNames()) {
	            System.out.println(name);
	        }

	        System.out.println(context.getBean(Bean2.class).getBean1());
	    }

	    // ⬇️较为经典的容器, 基于 java 配置类来创建, 用于 web 环境
	    public static void testAnnotationConfigServletWebServerApplicationContext() {
	        AnnotationConfigServletWebServerApplicationContext context =
	                new AnnotationConfigServletWebServerApplicationContext(WebConfig.class);
	        for (String name : context.getBeanDefinitionNames()) {
	            System.out.println(name);
	        }
	    }
	 
	 
	/*****
	     *   BeanPostProcessor后处理器  处理@Autowired @Resource ...  使autowired起作用
	     */
	    public static void test4() {
	    	 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();//创建bean工厂
	         // bean 的定义（class, scope, 初始化, 销毁）
	         AbstractBeanDefinition beanDefinition =//BeanDefinitionBuilder   bean定义构建类创建bean定义   得到bean定义
	                 BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
	         beanFactory.registerBeanDefinition("config", beanDefinition);//bean工厂 注册bean定义  第一个参数是名称  第二个参数是bean定义对象
	         
	         // 给 BeanFactory 添加一些常用的后处理器
	         AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
	         
	       //bean工厂通过bean的类型方法得到bean后置处理器 ，把bean后置处理器注册到bean工厂中
	         beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().stream().forEach(beanPostProcessor->{
	      	   beanPostProcessor.postProcessBeanFactory(beanFactory);
	         });
	         
	         // Bean 后处理器, 针对 bean 的生命周期的各个阶段提供扩展, 例如 @Autowired @Resource ...
//	         beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanFactory::addBeanPostProcessor);
	        
	         /***** 
	         beanFactory.getBeansOfType(BeanPostProcessor.class).values().forEach(beanPostProcessor->{
	        	 System.out.println("*************"+beanPostProcessor);
	        	 //打印bean的后处理器  看看先后顺序  测试autowire和resource哪个先加载  
	        	 // AutowiredAnnotationBeanPostProcessor加载autowird
	        	 //CommonAnnotationBeanPostProcessor 加载resource
	        	 beanFactory.addBeanPostProcessor(beanPostProcessor);
	         });
	         *****/
	         
	         beanFactory.getBeansOfType(BeanPostProcessor.class).values().stream()
	         .sorted(beanFactory.getDependencyComparator()).//添加比较器  可以控制加载后处理器的顺序
	         forEach(beanPostProcessor->{
	        	 System.out.println("*************"+beanPostProcessor);
	        	 //打印bean的后处理器  看看先后顺序  测试autowire和resource哪个先加载  
	        	 beanFactory.addBeanPostProcessor(beanPostProcessor);
	         });
	        
	         beanFactory.preInstantiateSingletons();
	         // 准备好所有单例   初始化bean单例的时机不同，如果打开的话，会先初始化bean的单例，构造器会先执行
	         System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>.");
	         System.out.println(beanFactory.getBean(Bean2.class));   
	        System.out.println(beanFactory.getBean(Bean2.class).getBean1()); 
	        
	      //  System.out.println(beanFactory.getBean(Bean1.class).getInter());
	    
	    }
	    
	    /*** 
	     *通过BeanFactoryPostProcessor后置处理器 可以解析bean的注解的类    解析@Bean
	     */
	    public static void test3() {
	      	 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();//创建bean工厂
	           // bean 的定义（class, scope, 初始化, 销毁）
	           AbstractBeanDefinition beanDefinition =//BeanDefinitionBuilder   bean定义构建类创建bean定义   得到bean定义
	                   BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
	           beanFactory.registerBeanDefinition("config", beanDefinition);//bean工厂 注册bean定义  第一个参数是名称  第二个参数是bean定义对象
	           
	           // 给 BeanFactory 添加一些常用的后处理器
	           AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
	           //bean工厂通过bean的类型方法得到bean后置处理器 ，把bean后置处理器注册到bean工厂中
	           beanFactory.getBeansOfType(BeanFactoryPostProcessor.class).values().stream().forEach(beanPostProcessor->{
	        	   beanPostProcessor.postProcessBeanFactory(beanFactory);
	           });
	           
	           //可以得到一些spring自定义的后处理器
	           for (String name:beanFactory.getBeanDefinitionNames()) {//循环bean定义名称
	   			System.out.println(name);
	   		}
	           
	           /****结果
	                config
					org.springframework.context.annotation.internalConfigurationAnnotationProcessor
					org.springframework.context.annotation.internalAutowiredAnnotationProcessor
					org.springframework.context.annotation.internalCommonAnnotationProcessor
					org.springframework.context.event.internalEventListenerProcessor
					org.springframework.context.event.internalEventListenerFactory
					bean1
					bean2
					bean3
					bean4
	            * ****/
	      }
	    
	    /***
	     * 给 BeanFactory 添加一些常用的后处理器
	     */
	    public static void test2() {
	   	 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();//创建bean工厂
	        // bean 的定义（class, scope, 初始化, 销毁）
	        AbstractBeanDefinition beanDefinition =//BeanDefinitionBuilder   bean定义构建类创建bean定义   得到bean定义
	                BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
	        beanFactory.registerBeanDefinition("config", beanDefinition);//bean工厂 注册bean定义  第一个参数是名称  第二个参数是bean定义对象
	        
	        // 给 BeanFactory 添加一些常用的后处理器
	        AnnotationConfigUtils.registerAnnotationConfigProcessors(beanFactory);
	        //可以得到一些spring自定义的后处理器
	        for (String name:beanFactory.getBeanDefinitionNames()) {//循环bean定义名称
				System.out.println(name);
			}
	        
	        /****结果
	            config
				org.springframework.context.annotation.internalConfigurationAnnotationProcessor
				org.springframework.context.annotation.internalAutowiredAnnotationProcessor
				org.springframework.context.annotation.internalCommonAnnotationProcessor
				org.springframework.context.event.internalEventListenerProcessor
				org.springframework.context.event.internalEventListenerFactory
	         * ****/
	   }
	    
	    /****
	     * 把bean定义注册到bean工厂 得到特定的bean定义的名称
	     */
	    public static void test1() {
	    	 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();//创建bean工厂
	         // bean 的定义（class, scope, 初始化, 销毁）
	         AbstractBeanDefinition beanDefinition =//BeanDefinitionBuilder   bean定义构建类创建bean定义   得到bean定义
	                 BeanDefinitionBuilder.genericBeanDefinition(Config.class).setScope("singleton").getBeanDefinition();
	         beanFactory.registerBeanDefinition("config", beanDefinition);//bean工厂 注册bean定义  第一个参数是名称  第二个参数是bean定义对象
	         for (String name:beanFactory.getBeanDefinitionNames()) {//循环bean定义名称
	 			System.out.println(name);
	 		}
	    }
	    
	    
	    /****
	     * 需要配置三个对象  必须有  可以创建一个web应用   可以访问路径 http://localhost:8080/hello 
	     *  ServletWebServerFactory(容器)   
	     * DispatcherServlet(前处理器  servlet web的入口) 
	     * DispatcherServletRegistrationBean(把servlet注册到tomcat服务器)
	     * @author Administrator
	     *
	     */
	    @Configuration
	    static class WebConfig {
	        @Bean
	        public ServletWebServerFactory servletWebServerFactory(){
	            return new TomcatServletWebServerFactory();
	        }
	        @Bean
	        public DispatcherServlet dispatcherServlet() {
	            return new DispatcherServlet();
	        }
	        @Bean
	        public DispatcherServletRegistrationBean registrationBean(DispatcherServlet dispatcherServlet) {
	            return new DispatcherServletRegistrationBean(dispatcherServlet, "/");
	        }
	        
	        //创建一个控制器  Controller接口
	        @Bean("/hello")
	        public org.springframework.web.servlet.mvc.Controller controller1() {
	            return (request, response) -> {
	                response.getWriter().print("hello");
	                return null;
	            };
	        }
	    }

	    @Configuration
	    static class Config {
	        @Bean
	        public Bean1 bean1() {
	            return new Bean1();
	        }

	        @Bean
	        public Bean2 bean2(Bean1 bean1) {
	            Bean2 bean2 = new Bean2();
	            bean2.setBean1(bean1);
	            return bean2;
	        }
	        
	        
		    @Bean
	        public Bean3 bean3() {
	            return new Bean3();
	        }

	        @Bean
	        public Bean4 bean4() {
	            return new Bean4();
	        }
	        
	    }

	    static class Bean1 {
	        
	       /***
	    	//@Resource(name="bean4")  resource如果不设置name  那么现根据类型，然后根据名称来查找  可以查找的到，如果设置name 那么会根据name来找对应的bean
	        private Inter bean3;
	        public Inter getInter() {
	            return bean3; 
	        }
	        **/
	    	
	    	/***
	    	// 这两个可以控制设置不同的bean
	        @Autowired
	        @Qualifier(value="bean3")
	        private Inter inter;
	        public Inter getInter() {
	            return inter; 
	        }
	        ***/
	    	  
	    	/****
	    	    //或者使用类型和已经定义好的bean的名称一样的bean 也可以
	    	    @Autowired
		        private Inter bean4;
		        public Inter getInter() {
		            return bean4; 
		        }
		       ***/ 
          /*****
	    	//如果两个注解都用的话，@Autowired会先起作用
	    	@Autowired
	    	@Resource(name="bean4")
	        private Inter bean3;
	        public Inter getInter() {
	            return bean3; 
	        }
	    	**/
	    	
	    }

	    static class Bean2 {

	        private Bean1 bean1;

	        public void setBean1(Bean1 bean1) {
	            this.bean1 = bean1;
	        }

	        public Bean1 getBean1() {
	            return bean1;
	        }
	    }
	    
	    interface Inter {

	    }

	    static class Bean3 implements Inter {

	    }

	    static class Bean4 implements Inter {

	    }
	    

	    
}

